package com.ikingtech.framework.sdk.security.filter;

import com.ikingtech.framework.sdk.authenticate.operation.IdentityValidator;
import com.ikingtech.framework.sdk.context.security.Identity;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.security.properties.SecurityProperties;
import com.ikingtech.framework.sdk.utils.Tools;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import static com.ikingtech.framework.sdk.context.constant.SecurityConstants.HEADER_CALLER;
import static com.ikingtech.framework.sdk.context.constant.SecurityConstants.HEADER_GATEWAY_REQUEST_ID;

/**
 * 请求拦截器
 *
 * @author tie yan
 */
@Slf4j
@RequiredArgsConstructor
public class SecurityFilter extends OncePerRequestFilter {

    private final IdentityValidator identityValidator;

    private final SecurityProperties properties;

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException {
        String requestUri = request.getRequestURI();

        Me.clear();

        String innerCall = request.getHeader(HEADER_CALLER);
        Identity identity;
        if (Tools.Str.isNotBlank(innerCall)) {
            // 内部调用，从请求头获取用户身份信息
            String requestId = request.getHeader(HEADER_GATEWAY_REQUEST_ID);
            identity = Tools.Json.toBean(this.identityValidator.getValidatedIdentity(requestId), Identity.class);
            if (identity != null) {
                identity.setRequestId(requestId);
            }
        } else {
            if (Tools.Http.pathMatches(requestUri, IdentityValidator.getDefaultIgnorePaths())) {
                //白名单路径,给一个默认用户的信息,直接放过
                Me.set(Identity.defaultUser());
                filterChain.doFilter(request, response);
                return;
            }
            if (Tools.Http.pathMatches(requestUri, this.properties.getIgnore())) {
                //同上,不过对比项换成了从properties配置文件中获取
                Me.set(Identity.defaultUser());
                filterChain.doFilter(request, response);
                return;
            }
            // 外部调用，为了适配Spring Cloud Gateway，将相关请求头封装到HttpHeaders
            HttpHeaders httpHeaders = new HttpHeaders();
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                httpHeaders.add(headerName, request.getHeader(headerName));
            }
            // 进行用户身份校验
            identity = this.identityValidator.validate(Tools.Coll.convertToMultiValueMap(request.getParameterMap()), httpHeaders);
        }
        if (null == identity) {
            // 校验失败，返回401
            this.unauthenticated(response);
            return;
        }
        // 用户身份信息获取成功，设置到请求上下文中
        Me.set(identity);

        filterChain.doFilter(request, response);
    }

    /**
     * 认证不通过写出到浏览器
     *
     * @param response 响应体
     * @throws IOException io异常
     */
    private void unauthenticated(HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json");
        PrintWriter writer = response.getWriter();
        writer.write(Tools.Json.toJsonStr(R.failed("loginExpired")));
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }
}
